

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>


#define MAXLINE 1024
#define TEST_URL "rtsp://192.168.3.103:554"
//#define TEST_URL "rtsp://odrm7.comcast.com:554"
//#define TEST_URL "rtsp://10.20.88.136:8800/sm?areaCode=3&resourceId=http://10.20.88.136:8880/DWHLS-1.m3u8"


const char * setupReq0 = \
	"SETUP rtsp://172.31.209.237:554 RTSP/1.0\r\n"
	"CSeq: %d\r\n"
	"OnDemandSessionId: f7cd7da988ffaecc68eda417e4e23e1f\r\n"
	"Require: com.comcast.ngod.s6\r\n"
	"Transport: MP2T/DVBC/UDP;unicast;client=8038bc0119f1;bandwidth=5000000;destination=192.168.112.134;client_port=529\r\n\r\n";

const char *setupRequest=\
"SETUP " TEST_URL " RTSP/1.0\r\n"
"CSeq: %d\r\n"
"OnDemandSessionId: be074250cc5a11d98cd50800200c9a66\r\n"
"Transport: MP2T/DVBC/UDP;unicast;client=00AF123456DE;bandwidth=2920263;destination=1.1.1.1;client_port=23\r\n\r\n";

const char *getParamRequest=\
"GET_PARAMETER " TEST_URL " RTSP/1.0\r\n"
"CSeq: %d\r\n"
"Session: %s\r\n\r\n";

const char *tearDownRequest=\
"TEARDOWN " TEST_URL " RTSP/1.0\r\n"
"CSeq: %d\r\n"
"Session: %s\r\n"
"OnDemandSessionId: be074250cc5a11d98cd50800200c9a66\r\n\r\n";

#define CMD_SETUP		0
#define CMD_GET_PARAM	1
#define CMD_TEARDOWN	2

#define MAX_THREAD   1000

typedef struct rtsp_io_s
{
	char send_buf[MAXLINE]; 
	int  send_cmd;    // send type: 0-setup,1-getp,2-teardown

	char recv_buf[MAXLINE];
	char session[33]; // get from recv
	int  seq;         // send value: add

	int issetup; // 0-not setup
}rtsp_io;

int do_rtsp_loop(struct sockaddr *servaddr);
int build_rtsp_request(rtsp_io *rio, char *szcmd);
int parse_keyvalue(char *kvalue, int kvaluelen, char *keyname, char *buf);


int main(int argc, char **argv)
{
	int i = 0;
	int ret = 0;
	struct sockaddr_in servaddr;
	unsigned short port;
	pthread_t tids[MAX_THREAD];
		
	if(argc < 3)
	{
		printf("usage: udpclient <IPaddress> [Port]\n");
		exit(1);
	}
	if(argc>=3)
	{
		sscanf(argv[2],"%d",(int *)&port);
	}


	/* init servaddr */
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(port);
	if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
	{
		printf("[%s] is not a valid IPaddress\n", argv[1]);
		exit(1);
	}
	printf("server IP:%s, PORT:%d\n",argv[1], port);

	for(i=0; i<MAX_THREAD; ++i)
	{
		ret = pthread_create(&tids[i],NULL,do_rtsp_loop,&servaddr);
		if(ret<0)
		{
			printf("create thread[%d] error! ret=%d\n", i, ret);
			break;
		}
	}
	
	//do_rtsp_loop(&servaddr);
	printf("=====create thread [%d] ok=====\n", i);
	//sleep(200);
	
	for(i=0; i<MAX_THREAD; ++i)
	{
		if(tids[i])
			pthread_join(tids[i],NULL);
	}
	printf("=====join threads [%d] ok=====\n", i);
	
	return 0;
}


int do_rtsp_loop(struct sockaddr *servaddr)
{
	int n;
	int i = 0;
	int ret = 0;
	char *cmd[]={"setup","getp","getp","teardown","getp"}; 
	rtsp_io rtspio;
	int sockfd;
	fd_set rfds;

	memset(&rtspio, 0, sizeof(rtspio));
	
	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	/* connect to server */
	if(connect(sockfd, servaddr, sizeof(struct sockaddr)) == -1)
	{
		perror("connect error!\n");
		return -5;
	}

	// send rtsp reqs 
	for(i=0; i<5; i++)
	{

		ret = build_rtsp_request(&rtspio, cmd[i]);
		if(ret<0) 
		{
			printf("wrong request ret=%d, not send.\n", ret);
			continue;
		}

		printf("---send data[%d]---\n", i);
		printf("%s",rtspio.send_buf);

		//send to server 
		n = write(sockfd, rtspio.send_buf, strlen(rtspio.send_buf));
		printf("\n*** %d size write to socket ***\n",n);

		FD_ZERO(&rfds);
		FD_SET(sockfd, &rfds);
		select(sockfd+1, &rfds, NULL, NULL, NULL);

		if(FD_ISSET(sockfd, &rfds))
		{
			/* receive data from server */
			// n = read(sockfd, recvline, MAXLINE);
			n =recv(sockfd, rtspio.recv_buf, MAXLINE,0);
			if( n == 0)
			{
				printf("peer has closed.\n");
				close(sockfd);
				exit(0);
			}
			else if(n == -1)
			{
				perror("read error");
				close(sockfd);
				exit(1);
			}
			else if(n > 0)
			{
				rtspio.recv_buf[n] = 0; /* terminate string */
				printf("---receive data[%d] ---\n%s", i, rtspio.recv_buf);

				if(rtspio.send_cmd==CMD_SETUP)
				{
					ret = parse_keyvalue((char*)rtspio.session, sizeof(rtspio.session), "Session", rtspio.recv_buf);
					if(ret>=0)
						rtspio.issetup = 1;
				}
				else if(rtspio.send_cmd==CMD_TEARDOWN)
				{
					rtspio.issetup = 0;
				}
			}
		}
		
		sleep(5);
	}
	
	return 0;
}

int parse_keyvalue(char *kvalue, int kvaluelen, char *keyname, char *buf)
{
	int ret = 0;
	char *tmp1 = NULL;
	char *tmp2 = NULL;
	char *tmp3 = NULL;
	int count = 0;

	if(!buf) return -1;

	tmp1 = strstr(buf, keyname);
	if(!tmp1){ ret = -12; goto eout; }

	tmp2 = strstr(tmp1, ":");
	if(!tmp2){ ret = -12; goto eout; }

	tmp3 = strstr(tmp2, "\r\n");
	if(!tmp3){ ret = -13; goto eout; }

	count = tmp3-tmp2-2;
	count = count<kvaluelen ? count:kvaluelen-1;
	strncpy(kvalue, tmp2+2, count);
	//printf("kname=%s, kvalue=%s, valuelen=%d\n", keyname, kvalue, strlen(kvalue));
	
	ret = 0;

eout:
	return ret;
}

int build_rtsp_request(rtsp_io *rio, char *szcmd)
{
	int ret = 0;
	if(!rio) return -1;
	if(!szcmd) return -2;

	if(strcmp(szcmd, "setup")==0)
	{
		rio->send_cmd = CMD_SETUP;
		//if(rio->issetup==0)
			sprintf(rio->send_buf, setupRequest, rio->seq++);
		//else
		//	ret = -11;
	}	
	else if(strcmp(szcmd, "getp")==0)
	{
		rio->send_cmd = CMD_GET_PARAM;
		if(rio->issetup!=0)
			sprintf(rio->send_buf, getParamRequest, rio->seq++, rio->session);
		else
			ret = -12;
	}
	else if(strcmp(szcmd, "teardown")==0)
	{
		rio->send_cmd = CMD_TEARDOWN;
		if(rio->issetup!=0)
			sprintf(rio->send_buf, tearDownRequest, rio->seq++, rio->session);
		else
			ret = -12;
	}
	else 
	{
		ret = -10;
	}

	return ret;
}